<template>
  <div class="card filter">
    <div class="flex justify-between margin-bottom-lg">
      <h4 class="title icon" v-if="title">
        {{ title }}
      </h4>
      <t-button variant="text" theme="primary" @click="appendCatalogue()">
        <template #icon>
          <component is="AddIcon" />
        </template>
        新增
      </t-button>
    </div>
    <t-input
      v-model="filterText"
      placeholder="按关键词搜索"
      class="search-input-group"
      @change="onInput"
    >
      <template #suffix-icon>
        <component is="SearchIcon" size="16" />
      </template>
    </t-input>
    <div class="t-scrollbar margin-top" style="height: calc(100% - 100px)">
      <t-tree
        ref="treeRef"
        class="hide-icon"
        :data="props.action != 'select' ? treeAllData : treeData"
        :keys="defaultKeys"
        :actived="Array.of(selected)"
        activable
        hover
        :icon="false"
        :filter="filterCatalogueByText"
        @click="catalogueClick"
      >
        <template #label="{ node }">
          <div
            class="flex justify-between align-center"
            @contextmenu.prevent="showRightMenu($event, node)"
          >
            <span class="label" v-show="!node.data.isEdit">{{
              node.label
            }}</span>
            <t-input
              v-show="node.data.isEdit"
              v-model="node.data.name"
              style="width: calc(100% - 30px)"
              @enter="appendCatalogue(node)"
            >
            </t-input>
            <span class="num" v-if="props.number">{{ node.data.nums }}</span>
          </div>
        </template>
      </t-tree>
      <t-card
        class="box-card"
        id="contextmenuList"
        size="small"
        style="display: none"
      >
        <div class="menu-item" name="edit" @click="editCatalogue">编辑</div>
        <div class="menu-item" name="delete" @click="removeCatalogue">删除</div>
      </t-card>
    </div>
  </div>
</template>
<script setup lang="ts">
import { ref, onBeforeMount } from 'vue';
import {
  MessagePlugin,
  DialogPlugin,
  type TreeInstanceFunctions,
  type TreeNodeModel,
  type TreeNodeState,
} from 'tdesign-vue-next';

// 接收父组件参数并设置默认值
interface TreeFilterProps {
  action?: any; // 操作的类型，base为增删改查基本操作都有,select为只显示，不做删改操作 ==> 非必传，默认为“base”
  requestApi?: (data?: any) => Promise<any>; // 请求分类数据的 api ==> 非必传
  data?: { [key: string]: any }[]; // 分类数据，如果有分类数据，则不会执行 api 请求 ==> 非必传
  title?: string; // treeFilter 标题 ==> 非必传
  defaultValue?: any; // 默认选中的值 ==> 非必传
  number?: boolean; //是否显示数量 ==>非必传，默认为true
  plusApi?: (data?: any) => Promise<any>; // 新增分类数据的 api ==> 非必传
  editApi?: (data?: any) => Promise<any>; // 修改分类数据的 api ==> 非必传
  minusApi?: (data?: any) => Promise<any>; // 移除分类数据的 api ==> 非必传
}
const props = withDefaults(defineProps<TreeFilterProps>(), {
  action: 'base',
  number: true,
});

const defaultKeys = {
  label: 'name',
  value: 'id',
};

const filterText = ref('');
const treeRef = ref<TreeInstanceFunctions>();
const treeData = ref<{ [key: string]: any }[]>([]);
const treeAllData = ref<{ [key: string]: any }[]>([]);
// 选中的值
const selected = ref();

onBeforeMount(async () => {
  // 重新接收一下默认值
  selected.value =
    typeof props.defaultValue === 'number' ? props.defaultValue : 0;

  // 有数据就直接赋值，没有数据就执行请求函数
  if (props.data?.length) {
    treeData.value = props.data;
    treeAllData.value = props.data;
    return;
  }
  const { data } = await props.requestApi!();
  if (data.code == 0) {
    data.data instanceof Array
      ? (treeData.value = data.data)
      : (treeData.value = data.data.lists);
    if (props.action === 'base') {
      if (props.number) {
        let total;
        total = treeData.value.reduce(
          (total: number, obj: any) => total + obj.nums ?? 0,
          0,
        );
        treeAllData.value = [
          { id: 0, name: '全部', nums: total },
          ...treeData.value,
        ];
      } else {
        treeAllData.value = [{ id: 0, name: '全部' }, ...treeData.value];
      }
    }
  } else {
    MessagePlugin.error(data.msg);
  }
});

//过滤
const filterCatalogueByText = ref();
const onInput = () => {
  filterCatalogueByText.value = (node: TreeNodeModel<TreeNodeState>) => {
    return node.label && node.label.indexOf(filterText.value) >= 0;
  };
};

//显示右键菜单
const currentNode = ref<TreeNodeModel>();
const showRightMenu = (event: MouseEvent, node: TreeNodeModel) => {
  const menu = document.getElementById('contextmenuList');
  if (menu) {
    menu.style.display = 'block';
    menu.style.top = event.clientY + 'px';
    menu.style.left = event.clientX + 'px';
    document.addEventListener('mouseup', hideRightMenu, false);
    currentNode.value = node;
    const actionElement: HTMLElement | null =
      document.querySelector("div[name='delete']");
    if (actionElement) {
      actionElement.style.display = currentNode.value?.data.flag_sys
        ? 'none'
        : 'block';
    }
  }
};
//隐藏右键菜单
const hideRightMenu = (e: any) => {
  if (e.button != 2) {
    // 0是左键、1是滚轮按钮或中间按钮（若有）、2鼠标右键
    const menu = document.getElementById('contextmenuList');
    if (menu) {
      menu.style.display = 'none'; // 菜单关闭
      currentNode.value?.setData(
        Object.assign(currentNode.value.data, { isEdit: false }),
      ); //解除编辑状态
    }
    document.removeEventListener('mouseup', hideRightMenu); // 及时解绑监听事件
  }
};

//编辑目录（当前节点转为编辑状态）
const editCatalogue = () => {
  currentNode.value?.setData(
    Object.assign(currentNode.value.data, { isEdit: true }),
  );
};

//添加目录(node有值即为编辑目录)
const appendCatalogue = async (node?: TreeNodeModel) => {
  let item: any = {};
  if (!node) {
    let c = treeRef.value?.getItems!().length;
    let val = '新增目录' + (c ? c + 1 : treeData.value.length + 1);
    item.name = val;
  } else {
    let regName = /^[0-9a-zA-Z\u4e00-\u9fa5 ]{2,15}$/;
    item.name = node.data.name;
    if (!regName.test(item.name)) {
      MessagePlugin.error('名称仅支持中文、数字、字母2~15个字符长度');
      return false;
    }
    node.data.isEdit = false;
    item.id = node.value;
  }
  if (Object.keys(item).length != 0) {
    try {
      const { data } = await props.plusApi!(item);
      if (data.code == 0) {
        if (!node) {
          treeRef.value?.appendTo!('', {
            id: parseInt(data.data.id),
            name: item.name,
            nums: 0,
          });
        } else {
          node.setData(item);
        }
        MessagePlugin.success(data.msg);
      } else {
        MessagePlugin.error(data.msg);
      }
    } catch (err: any) {
      MessagePlugin.error(err.msg);
    }
  }
};

//删除目录
const removeCatalogue = () => {
  const confirmDia = DialogPlugin({
    header: '提示',
    body: '删除后将不可恢复，确认继续操作？',
    onConfirm: async ({ e }) => {
      let chooseId = currentNode.value?.value;
      const { data } = await props.minusApi!({ id: chooseId });
      if (data.code == 0) {
        chooseId && treeRef.value?.remove!(chooseId);
      } else {
        MessagePlugin.error(data.msg);
      }
      confirmDia.hide();
    },
    onClose: ({ e, trigger }) => {
      confirmDia.hide();
    },
  });
};

interface FilterEmits {
  (e: 'change', value: any): void;
}
const emit = defineEmits<FilterEmits>();

//选中
const catalogueClick = ({ node }: any) => {
  if (selected.value != node.value) {
    selected.value = node.value;
    emit('change', node.value);
  }
};

// 暴露给父组件使用
defineExpose({ treeData, selected });
</script>
<style lang="scss" scoped>
.box-card {
  position: fixed;
  z-index: 9999;
  .menu-item {
    display: block;
    font-size: 14px;
    line-height: 28px;
    @include font_color('font_color');
    cursor: pointer;
    margin: 0 -16px;
    padding: 0 16px;

    &:hover {
      @include themeify {
        background-color: themed('second_container_color');
        color: themed('primary_color');
      }
    }
  }
}
</style>
